Java 26일 코스 - Day 22: 빌드 도구 Maven/Gradle

Day 22: 빌드 도구 Maven/Gradle

빌드 도구는 프로젝트의 의존성 관리, 컴파일, 테스트, 패키징을 자동화합니다. 레고 블록에 비유하면, 필요한 부품(라이브러리)을 자동으로 가져오고 조립(빌드)하는 로봇과 같습니다. Java 생태계에서는 Maven과 Gradle이 양대 산맥입니다.

Maven 프로젝트 구조와 pom.xml

Maven은 XML 기반의 빌드 도구로, 규칙 기반(Convention over Configuration) 철학을 따릅니다.

// Maven 표준 디렉토리 구조:
// my-project/
// ├── pom.xml                      <- 프로젝트 설정 (핵심)
// ├── src/
// │   ├── main/
// │   │   ├── java/                <- 소스 코드
// │   │   │   └── com/example/
// │   │   │       └── App.java
// │   │   └── resources/           <- 설정 파일, 정적 리소스
// │   │       └── application.properties
// │   └── test/
// │       ├── java/                <- 테스트 코드
// │       │   └── com/example/
// │       │       └── AppTest.java
// │       └── resources/           <- 테스트 리소스
// └── target/                      <- 빌드 결과물 (자동 생성)

// pom.xml 예시 (핵심 부분):
/*
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>my-project</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.10.1</version>
        </dependency>
    </dependencies>
</project>
*/

public class MavenProjectDemo {
    public static void main(String[] args) {
        // Maven 주요 명령어:
        // mvn clean          - target 폴더 삭제
        // mvn compile        - 소스 코드 컴파일
        // mvn test           - 테스트 실행
        // mvn package        - JAR/WAR 패키징
        // mvn install        - 로컬 저장소에 설치
        // mvn clean package  - 정리 + 패키징 (가장 많이 사용)

        // Maven 생명주기:
        // validate -> compile -> test -> package -> verify -> install -> deploy

        System.out.println("Maven 프로젝트 구조를 이해했습니다!");
        System.out.println("의존성은 pom.xml의 <dependencies>에 선언합니다.");
    }
}

Gradle 프로젝트 구조와 build.gradle

Gradle은 Groovy/Kotlin DSL 기반의 현대적 빌드 도구로, 더 간결하고 유연합니다.

// Gradle 프로젝트 구조:
// my-project/
// ├── build.gradle(.kts)            <- 빌드 스크립트 (핵심)
// ├── settings.gradle(.kts)         <- 프로젝트 설정
// ├── gradle/
// │   └── wrapper/
// │       ├── gradle-wrapper.jar    <- Gradle 래퍼
// │       └── gradle-wrapper.properties
// ├── gradlew / gradlew.bat        <- 래퍼 실행 스크립트
// ├── src/
// │   ├── main/java/               <- 소스 (Maven과 동일)
// │   ├── main/resources/
// │   ├── test/java/
// │   └── test/resources/
// └── build/                        <- 빌드 결과물

// build.gradle.kts (Kotlin DSL) 예시:
/*
plugins {
    java
    application
}

group = "com.example"
version = "1.0.0"

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.google.code.gson:gson:2.10.1")
    implementation("org.slf4j:slf4j-api:2.0.9")

    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

application {
    mainClass.set("com.example.App")
}

tasks.test {
    useJUnitPlatform()
}
*/

public class GradleProjectDemo {
    public static void main(String[] args) {
        // Gradle 주요 명령어:
        // ./gradlew build         - 전체 빌드
        // ./gradlew clean         - build 폴더 삭제
        // ./gradlew test          - 테스트 실행
        // ./gradlew run           - 애플리케이션 실행
        // ./gradlew dependencies  - 의존성 트리 확인
        // ./gradlew tasks         - 사용 가능한 태스크 목록

        System.out.println("Gradle은 Maven보다 빠르고 간결합니다!");
        System.out.println("./gradlew 래퍼를 사용하면 Gradle 설치 없이 빌드 가능합니다.");
    }
}

의존성 관리

외부 라이브러리를 프로젝트에 추가하고 관리하는 방법입니다.

// 의존성 스코프 비교:
//
// Maven           | Gradle              | 설명
// compile (기본)  | implementation       | 컴파일+런타임에 필요
// provided        | compileOnly          | 컴파일에만 필요 (런타임에 제공됨)
// runtime         | runtimeOnly          | 런타임에만 필요
// test            | testImplementation   | 테스트에만 필요
// -               | api                  | 의존성을 외부에 노출 (라이브러리용)

// 자주 사용하는 의존성 예시 (Gradle Kotlin DSL):
/*
dependencies {
    // JSON 처리
    implementation("com.google.code.gson:gson:2.10.1")
    implementation("com.fasterxml.jackson.core:jackson-databind:2.15.2")

    // 로깅
    implementation("ch.qos.logback:logback-classic:1.4.11")

    // HTTP 클라이언트
    implementation("com.squareup.okhttp3:okhttp:4.12.0")

    // 데이터베이스
    implementation("com.h2database:h2:2.2.224")

    // Lombok (코드 생성)
    compileOnly("org.projectlombok:lombok:1.18.30")
    annotationProcessor("org.projectlombok:lombok:1.18.30")

    // 테스트
    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
    testImplementation("org.assertj:assertj-core:3.24.2")
    testImplementation("org.mockito:mockito-core:5.7.0")
}
*/

import java.util.Map;

public class DependencyManagement {
    public static void main(String[] args) {
        // 의존성 충돌 해결:
        // - Maven: <exclusions>로 제외, <dependencyManagement>로 버전 통일
        // - Gradle: exclude로 제외, constraints로 버전 강제

        // BOM (Bill of Materials): 관련 라이브러리 버전을 한 번에 관리
        // Spring Boot BOM, JUnit BOM 등

        Map<String, String> comparison = Map.of(
            "설정 파일", "Maven: XML / Gradle: Groovy/Kotlin",
            "빌드 속도", "Gradle이 2~10배 빠름 (캐싱/증분 빌드)",
            "유연성", "Gradle이 더 유연 (코드로 빌드 로직 작성)",
            "학습 곡선", "Maven이 더 단순 (규칙 기반)",
            "채택률", "Spring Boot, Android는 Gradle 권장"
        );

        System.out.println("=== Maven vs Gradle 비교 ===");
        comparison.forEach((key, value) ->
            System.out.printf("%-12s: %s%n", key, value));
    }
}

멀티 모듈 프로젝트

대규모 프로젝트를 여러 모듈로 분리하는 구조입니다.

// 멀티 모듈 Gradle 프로젝트 구조:
// my-app/
// ├── settings.gradle.kts
// │   -> include("app-core", "app-api", "app-web")
// ├── build.gradle.kts (루트: 공통 설정)
// ├── app-core/
// │   ├── build.gradle.kts
// │   └── src/main/java/...
// ├── app-api/
// │   ├── build.gradle.kts
// │   └── src/main/java/...
// └── app-web/
//     ├── build.gradle.kts
//     └── src/main/java/...

// 루트 build.gradle.kts:
/*
subprojects {
    apply(plugin = "java")

    group = "com.example"
    version = "1.0.0"

    java {
        sourceCompatibility = JavaVersion.VERSION_17
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
    }

    tasks.test {
        useJUnitPlatform()
    }
}
*/

// app-api/build.gradle.kts:
/*
dependencies {
    implementation(project(":app-core"))  // 다른 모듈 의존
    implementation("org.springframework.boot:spring-boot-starter-web:3.2.0")
}
*/

public class MultiModuleProject {
    public static void main(String[] args) {
        System.out.println("멀티 모듈 프로젝트의 장점:");
        System.out.println("1. 관심사 분리 (core, api, web)");
        System.out.println("2. 독립적 빌드와 테스트");
        System.out.println("3. 의존성 명확한 관리");
        System.out.println("4. 변경된 모듈만 재빌드 (속도)");
        System.out.println("5. 팀별 독립 개발 가능");
    }
}

오늘의 연습문제

  1. Gradle 프로젝트 생성: ./gradlew init --type java-application 또는 수동으로 Gradle Java 프로젝트를 만들고, Gson 의존성을 추가하여 JSON 파싱 프로그램을 작성하세요. ./gradlew run으로 실행하세요.

  2. Maven vs Gradle 변환: 주어진 Maven pom.xml을 Gradle build.gradle.kts로 변환하세요. 동일한 의존성, 플러그인, 설정을 유지하세요.

  3. 커스텀 Gradle 태스크: build.gradle.kts에 커스텀 태스크를 만드세요. ./gradlew generateReport 명령으로 프로젝트의 소스 파일 수, 총 코드 줄 수, 의존성 목록을 텍스트 파일로 출력하는 태스크를 작성하세요.

이 글이 도움이 되었나요?